package de.latzko.pattern.math

import scalala.tensor.dense.DenseVector._
import scalala.tensor.dense.DenseVector
import java.util.ArrayList._
import scalala.Scalala._
import scalala.Scalala

trait NearestNeighbour {
	
	var minAverageDistance:Double = Double.MaxValue;
	var maxAverageDistance:Double = 0;
	/**
	 * Find the k nearest neighbors from vector vec out of list vectors 
	 * @param vec
	 * @param vectors
	 * @param k
	 * @param metric
	 * @return array with indeces of the k nearest neighbors
	 */
	def knn(vec:DenseVector ,vectors:Array[DenseVector],
			k:Int,metric:DenseVector=> Double = {v1:DenseVector => norm(v1,2)}):Array[Int] = {
		return knnPlusDist(vec,vectors,k,metric).map(x => x._2 )
		
	}
	
	/**
	 * returns k nearest neighbours with distance in addition
	 * @param vec
	 * @param vectors
	 * @param k
	 * @param metric
	 * @return
	 */
	def knnPlusDist(vec:DenseVector ,vectors:Array[DenseVector],
			k:Int,metric:DenseVector=> Double = {v1:DenseVector => norm(v1,2)}):Array[(Double,Int)]= {
		
		//find neighbors
		var i:Int = -1;
		var distance = vectors.map(x => {i =i+1 ;(metric(vec-x),i)})
		distance = distance.sortWith((e1,e2)=> (e1._1 < e2._1 )).slice(1, k+1)
		
		//set min or max average
		val avr  = distance.reduceLeft((x,y) => (x._1 + y._1,1))._1 / k ; 
		if(avr > maxAverageDistance )
			maxAverageDistance  = avr;
		if(minAverageDistance  > avr )
			minAverageDistance  = avr;
		
		return distance
		
	}
	
	
	/**
	 * Find all neares neighbors and returns an array of arrays 
	 * @param vectors
	 * @param k
	 * @param metric
	 * @return
	 */
	def allKnn(vectors:Array[DenseVector],k:Int,
			metric:DenseVector=> Double = {v1:DenseVector => norm(v1,2)}):Array[Array[Int]] = {
		return vectors.map(x =>knn(x,vectors,k,metric))
	}

}